package com.frank.http.encryp;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.UUID;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;

import com.frank.http.base.RequestParams;
import com.frank.http.expand.Logger;

/**
 * 工具类
 * 
 * @author Frank
 * @version 1.0 Create by 2016.1.21
 */
public class EncrypUtils {

	private final static String TAG = EncrypUtils.class.getSimpleName();

	private static final String DEFAULT_ENCODING = "UTF-8";

	private static BitSet URI_UNRESERVED_CHARACTERS = new BitSet();

	private static BitSet CONTENT_UNRESERVED_CHARACTERS = new BitSet();

	private static String[] PERCENT_ENCODED_STRINGS = new String[256];

	static {
		for (int i = 'a'; i <= 'z'; i++) {
			URI_UNRESERVED_CHARACTERS.set(i);
			CONTENT_UNRESERVED_CHARACTERS.set(i);
		}
		for (int i = 'A'; i <= 'Z'; i++) {
			URI_UNRESERVED_CHARACTERS.set(i);
			CONTENT_UNRESERVED_CHARACTERS.set(i);
		}
		for (int i = '0'; i <= '9'; i++) {
			URI_UNRESERVED_CHARACTERS.set(i);
			CONTENT_UNRESERVED_CHARACTERS.set(i);
		}

		URI_UNRESERVED_CHARACTERS.set('-');
		URI_UNRESERVED_CHARACTERS.set('.');
		URI_UNRESERVED_CHARACTERS.set('_');
		URI_UNRESERVED_CHARACTERS.set('~');

		CONTENT_UNRESERVED_CHARACTERS.set('-');
		CONTENT_UNRESERVED_CHARACTERS.set('_');
		CONTENT_UNRESERVED_CHARACTERS.set('.');
		CONTENT_UNRESERVED_CHARACTERS.set('*');

		for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) {
			PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
		}
	}

	/**
	 * 获取手机mac地址 错误返回12个0
	 */
	public static String getMacAddress(Context context) {
		if (VERSION.SDK_INT <= VERSION_CODES.ICE_CREAM_SANDWICH) {
			return getDeviceId(context);
		}
		// 获取mac地址：
		String macAddress = "000000000000";
		try {
			WifiManager wifiMgr = (WifiManager) context
					.getSystemService(Context.WIFI_SERVICE);
			WifiInfo info = (null == wifiMgr ? null : wifiMgr
					.getConnectionInfo());
			if (null != info) {
				if (!TextUtils.isEmpty(info.getMacAddress()))
					macAddress = info.getMacAddress().replace(":", "");
				else
					return macAddress;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return macAddress;
		}
		return macAddress;
	}

	/**
	 * 获得设备id
	 * 
	 * @return
	 */
	public static String getDeviceId(Context context) {
		TelephonyManager mTm = (TelephonyManager) context
				.getSystemService(Context.TELEPHONY_SERVICE);
		return mTm.getDeviceId();
	}

	/**
	 * 获得屏幕尺寸
	 * 
	 * @return
	 */
	public static String getDisplay(Context context) {
		String display = "480*720";
		DisplayMetrics metric = new DisplayMetrics();
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		wm.getDefaultDisplay().getMetrics(metric);
		int width = metric.widthPixels; // 屏幕宽度（像素）
		int height = metric.heightPixels; // 屏幕高度（像素）
		display = width + "*" + height;
		return display;
	}

	/**
	 * 获得屏幕宽
	 * 
	 * @return
	 */
	public static int getDisplayWidth(Context context) {
		DisplayMetrics metric = new DisplayMetrics();
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		wm.getDefaultDisplay().getMetrics(metric);
		int width = metric.widthPixels; // 屏幕宽度（像素）
		return width;
	}

	/**
	 * 获得屏幕高
	 * 
	 * @return
	 */
	public static int getDisplayHeight(Context context) {
		DisplayMetrics metric = new DisplayMetrics();
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		wm.getDefaultDisplay().getMetrics(metric);
		int height = metric.heightPixels; // 屏幕高度
		return height;
	}

	/**
	 * 获得手机型号
	 * 
	 * @return
	 */
	public static String getModel() {
		return android.os.Build.MODEL;
	}

	/**
	 * 获得sdk版本号
	 * 
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public static String getVersionSdk() {
		return android.os.Build.VERSION.SDK;
	}

	/**
	 * 获得系统版本号
	 * 
	 * @return
	 */
	public static String getVersionRelease() {
		return android.os.Build.VERSION.RELEASE;
	}

	/**
	 * 获得唯一标志
	 * 
	 * @return
	 */
	public static String getUid(Context context) {
		String m1 = get32MD5(getMacAddress(context));
		String m2 = get32MD5(m1 + getMacAddress(context) + getModel());
		String m3 = get32MD5(getVersionRelease() + m1 + m2);
		String m4 = get32MD5(m3 + m1 + getDisplay(context) + m2);
		String m5 = get32MD5(m1 + m2 + m4 + getMacAddress(context) + m3);
		// Logger.i(TAG, "m1" + m1);
		// Logger.i(TAG, "m2" + m2);
		// Logger.i(TAG, "m3" + m3);
		// Logger.i(TAG, "m4" + m4);
		// Logger.i(TAG, "m5" + m5);
		String uid = m4.substring(4, 8) + "-" + m1.substring(8, 16) + "-"
				+ m1.substring(0, 4) + "-" + m1.substring(4, 8) + "-"
				+ m2.substring(0, 4) + "-" + m3.substring(8, 16)
				+ m4.substring(0, 4) + "-" + m5.substring(8, 16);
		// Logger.i(TAG, "uid" + uid);
		return uid;
	}

	/**
	 * md5签名
	 * 
	 * @param s
	 * @return
	 */
	public static String get32MD5(String s) {
		char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
				'a', 'b', 'c', 'd', 'e', 'f' };
		try {
			byte[] strTemp = s.getBytes();
			// 使用MD5创建MessageDigest对象
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(strTemp);
			byte[] md = mdTemp.digest();
			int j = md.length;
			char str[] = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte b = md[i];
				// System.out.println((int)b);
				// 将没个数(int)b进行双字节加密
				str[k++] = hexDigits[b >> 4 & 0xf];
				str[k++] = hexDigits[b & 0xf];
			}
			return new String(str);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * md5签名-->base64
	 * 
	 * @param s
	 * @return
	 */
	public static String get128MD5ToBase64(String s) {
		try {
			byte[] strTemp = s.getBytes();
			// 使用MD5创建MessageDigest对象
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(strTemp);
			byte[] encodeBytes = Base64.encode(mdTemp.digest(), Base64.DEFAULT);
			return new String(encodeBytes).toString().trim();
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * md5签名-->base64
	 * 
	 * @param s
	 * @return
	 */
	public static String get128MD5ToBase64(ByteArrayOutputStream out) {
		try {
			byte[] strTemp = out.toByteArray();
			// 使用MD5创建MessageDigest对象
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(strTemp);
			byte[] encodeBytes = Base64.encode(mdTemp.digest(), Base64.DEFAULT);
			return new String(encodeBytes).toString().trim();
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 获得uuid
	 * 
	 * @return
	 */
	public static String getUUId() {
		return UUID.randomUUID().toString();
	}

	/***
	 * 获得格林时间
	 * 
	 * @return
	 */
	public static String getUTC() {
		Calendar cd = Calendar.getInstance();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",
				Locale.US);
		sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // 设置时区为GMT
		return sdf.format(cd.getTime());
	}

	/***
	 * 获得authAstring
	 * 
	 * @param session_request_id
	 * @param session_id
	 * @param session_card
	 * @param sign_utc_time
	 * @param expired_time_offset
	 * @return
	 */
	public static String getAuthString(String vsersion,String session_request_id,
			String session_id, String session_card, String sign_utc_time,
			String expired_time_offset) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(vsersion);
		stringBuffer.append("/");
		stringBuffer.append(session_request_id);
		stringBuffer.append("/");
		stringBuffer.append(session_id);
		stringBuffer.append("/");
		stringBuffer.append(session_card);
		stringBuffer.append("/");
		stringBuffer.append(sign_utc_time);
		stringBuffer.append("/");
		stringBuffer.append(expired_time_offset);
		return stringBuffer.toString();
	}
	
	/***
	 * 获得authAstring
	 * 
	 * @param session_request_id
	 * @param session_id
	 * @param session_card
	 * @param sign_utc_time
	 * @param expired_time_offset
	 * @return
	 */
	public static String getAuthString(String session_request_id,
			String session_id, String session_card, String sign_utc_time,
			String expired_time_offset) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("session-init-v1");
		stringBuffer.append("/");
		stringBuffer.append(session_request_id);
		stringBuffer.append("/");
		stringBuffer.append(session_id);
		stringBuffer.append("/");
		stringBuffer.append(session_card);
		stringBuffer.append("/");
		stringBuffer.append(sign_utc_time);
		stringBuffer.append("/");
		stringBuffer.append(expired_time_offset);
		return stringBuffer.toString();
	}

	/***
	 * 获得authAstring
	 * 
	 * @param session_request_id
	 * @param session_id
	 * @param session_card
	 * @param sign_utc_time
	 * @param expired_time_offset
	 * @return
	 */
	public static String getAuthStringApp(String session_request_id,
			String session_id, String session_card, String sign_utc_time,
			String expired_time_offset) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("app-auth-v1");
		stringBuffer.append("/");
		stringBuffer.append(session_request_id);
		stringBuffer.append("/");
		stringBuffer.append(session_id);
		stringBuffer.append("/");
		stringBuffer.append(session_card);
		stringBuffer.append("/");
		stringBuffer.append(sign_utc_time);
		stringBuffer.append("/");
		stringBuffer.append(expired_time_offset);
		return stringBuffer.toString();
	}

	/**
	 * 获得authAstring
	 * 
	 * @param auth_string
	 * @param interference
	 * @return
	 */
	public static String getAuthString(String auth_string, String interference) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(auth_string);
		stringBuffer.append("/");
		stringBuffer.append(interference);
		return stringBuffer.toString();
	}

	/**
	 * 获得authAstring
	 * 
	 * @param auth_string
	 * @param interference
	 * @return
	 */
	public static String getAuthString(String auth_string, String type,
			String path, String data) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(auth_string);
		stringBuffer.append("/");
		stringBuffer.append(type);
		stringBuffer.append("/");
		stringBuffer.append(path);
		stringBuffer.append("/");
		stringBuffer.append(data);
		return stringBuffer.toString();
	}

	/**
	 * 获得authAstring
	 * 
	 * @param auth_string
	 * @param signedHeaders
	 * @param signature
	 * @return
	 */
	public static String getAuthString(String auth_string,
			String signedHeaders, String signature) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(auth_string);
		stringBuffer.append("/");
		stringBuffer.append(signedHeaders);
		stringBuffer.append("/");
		stringBuffer.append(signature);
		return stringBuffer.toString();
	}

	/***
	 * 获得signing_key
	 * 
	 * @return
	 */
	public static String getSigningKey(String auth_string, String session_key) {
		String signKey = "";
		try {
			signKey = encodeHmacSHA256(auth_string.getBytes(),
					session_key.getBytes());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return signKey;
	}

	/**
	 * 产生HmacSHA256摘要算法的密钥
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static byte[] initHmacSHA256Key() throws NoSuchAlgorithmException {
		// 初始化HmacMD5摘要算法的密钥产生器
		KeyGenerator generator = KeyGenerator.getInstance("HmacSHA256");
		// 产生密钥
		SecretKey secretKey = generator.generateKey();
		// 获得密钥
		byte[] key = secretKey.getEncoded();
		return key;
	}

	/**
	 * HmacSHA1摘要算法 对于给定生成的不同密钥，得到的摘要消息会不同，所以在实际应用中，要保存我们的密钥
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String encodeHmacSHA256(byte[] data, byte[] key)
			throws Exception {
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");// 还原密钥
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());// 实例化Mac
		mac.init(secretKey);// 初始化mac
		byte[] digest = mac.doFinal(data);// 执行消息摘要
		return Hex.toHexString(digest);// 转为十六进制的字符串
	}

	/**
	 * RFC
	 * 3986规定，"URI非保留字符"包括以下字符：字母（A-Z，a-z）、数字（0-9）、连字号（-）、点号（.）、下划线（_)、波浪线（~）
	 * ，算法实现如下： 1. 将字符串转换成UTF-8编码的字节流 2. 保留所有“URI非保留字符”原样不变 3. 对其余字节做一次RFC
	 * 3986中规定的百分号编码（Percent-encoding），即一个“%”后面跟着两个表示该字节值的十六进制字母，字母一律采用大写形式。
	 * 
	 * @param value
	 * @return
	 */
	public static String uriEncode(String value) {
		try {
			StringBuilder builder = new StringBuilder();
			for (byte b : value.getBytes(DEFAULT_ENCODING)) {
				if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
					builder.append((char) b);
				} else {
					builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
				}
			}
			return builder.toString();
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * utf字符
	 * 
	 * @param value
	 * @return
	 */
	public static String encodeUtf(String value) {
		try {
			StringBuilder builder = new StringBuilder();
			for (byte b : value.getBytes(DEFAULT_ENCODING)) {
				if (CONTENT_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
					builder.append((char) b);
				} else {
					builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
				}
			}
			return builder.toString();
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * UriEncodeExceptSlash()与UriEncode()
	 * 类似，区别是斜杠（/）不做编码。一个简单的实现方式是先调用UriEncode()，然后把结果中所有的`%2F`都替换为`/`
	 * 
	 * @param path
	 * @return
	 */
	public static String normalizePath(String path) {
		return uriEncode(path).replace("%2F", "/");
	}

	/***
	 * 生成CanonicalRequest
	 * 
	 * @param httpMethod
	 * @param canonicalURI
	 * @param canonicalQueryString
	 * @param canonicalHeaders
	 * @return
	 */
	public static String getCanonicalRequest(String httpMethod,
			String canonicalURI, String canonicalQueryString,
			String canonicalHeaders) {
		String str = httpMethod + "\n" + canonicalURI + "\n"
				+ canonicalQueryString + "\n" + canonicalHeaders;
		Logger.i("getCanonicalRequest", str);
		return str;
	}

	/**
	 * 获得CanonicalURI
	 * 
	 * @param path
	 * @return
	 */
	public static String getCanonicalURI(String path) {
		if (path == null) {
			return "/";
		} else if (path.startsWith("/")) {
			return normalizePath(path);
		} else {
			return "/" + normalizePath(path);
		}
	}

	/**
	 * 获得CanonicalQueryString
	 * 
	 * @param parameters
	 * @return
	 */
	public static String getCanonicalQueryString(
			SortedMap<Object, Object> parameters) {
		SortedMap<Object, Object> uriEncodeParameters = new TreeMap<Object, Object>();
		Set uriEncodeSet = parameters.entrySet();// 所有参与传参的参数按照accsii排序（升序）
		Iterator uriEncodeIt = uriEncodeSet.iterator();
		while (uriEncodeIt.hasNext()) {
			Map.Entry entry = (Map.Entry) uriEncodeIt.next();
			String k = (String) entry.getKey();
			Object v = entry.getKey();
			if (null != v) {
				String value = (String) v;
				uriEncodeParameters.put(uriEncode(k), uriEncode(value));
			}
		}
		StringBuffer sb = new StringBuffer();
		Set es = uriEncodeParameters.entrySet();// 所有参与传参的参数按照accsii排序（升序）
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}

	/**
	 * 获得CanonicalQueryString
	 * 
	 * @param parameters
	 * @return
	 */
	public static String getCanonicalQueryString(String url) {
		String urlStr = "";
		if (url.contains("?")) {
			urlStr = url.substring(url.indexOf("?") + 1, url.length());
		}
		if (urlStr.equals("")) {
			return "";
		}
		String[] parametersStrings = urlStr.split("&");

		SortedMap<Object, Object> uriEncodeParameters = new TreeMap<Object, Object>();
		if (parametersStrings != null && parametersStrings.length > 0) {
			for (int i = 0; i < parametersStrings.length; i++) {
				Logger.i(TAG, "parametersStrings[i]:" + parametersStrings[i]);
				String[] strings = parametersStrings[i].split("=");
				Log.i(TAG, "strings.length:" + strings.length);
				if (strings[0] != null && !strings[0].equals("")) {
					if (strings.length > 1) {
						uriEncodeParameters.put(uriEncode(strings[0]),
								uriEncode(strings[1]));
					} else {
						uriEncodeParameters.put(uriEncode(strings[0]),
								uriEncode(""));
					}
				}
			}
		}
		StringBuffer sb = new StringBuffer();
		Set es = uriEncodeParameters.entrySet();// 所有参与传参的参数按照accsii排序（升序）
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.deleteCharAt(sb.length() - 1);
		Logger.i("sb.toString()", sb.toString());
		return sb.toString();
	}

	/**
	 * 获得CanonicalQueryString
	 * 
	 * @param parameters
	 * @return
	 */
	public static String getCanonicalHeaders(
			SortedMap<String, String> parameters) {
		StringBuffer stringBuffer = new StringBuffer();
		if (parameters.isEmpty()) {
			return "";
		}

		LinkedList<String> headerStrings = new LinkedList<String>();
		for (Entry<String, String> entry : parameters.entrySet()) {
			String key = (String) entry.getKey();
			if (key == null) {
				continue;
			}
			String value = (String) entry.getValue();
			if (value == null) {
				value = "";
			}
			headerStrings.add(uriEncode(key.toLowerCase()) + ':'
					+ uriEncode(value.trim()));
		}
		Collections.sort(headerStrings);

		for (int i = 0; i < headerStrings.size(); i++) {
			stringBuffer.append(headerStrings.get(i));
			if (i != headerStrings.size() - 1) {
				stringBuffer.append("\n");
			}
		}

		return stringBuffer.toString();
	}

	/**
	 * 获得SignedHeaders
	 * 
	 * @param parameters
	 * @return
	 */
	public static String getSignedHeaders(SortedMap<String, String> parameters) {
		StringBuffer stringBuffer = new StringBuffer();
		if (parameters.isEmpty()) {
			return "";
		}

		LinkedList<String> headerStrings = new LinkedList<String>();
		for (Entry<String, String> entry : parameters.entrySet()) {
			String key = (String) entry.getKey();
			if (key == null) {
				continue;
			}
			String value = (String) entry.getValue();
			if (value == null) {
				value = "";
			}
			headerStrings.add(uriEncode(key.toLowerCase()));
		}
		Collections.sort(headerStrings);

		for (int i = 0; i < headerStrings.size(); i++) {
			stringBuffer.append(headerStrings.get(i));
			if (i != headerStrings.size() - 1) {
				stringBuffer.append(";");
			}
		}

		return stringBuffer.toString();
	}
}
